和entity的作用相同,但由於我們使用NoSQL,所以改稱呼為document。
collation用來指定使用的資料表名稱
//Customer.java
@Document(collection = "customer")
public class Customer {
@Id
private String id;
當留空時會出現錯誤,message是提供給前端顯示的訊息
@NotEmpty(message = "名字為必填項目")
private String firstName;
@NotEmpty(message = "姓氏為必填項目")
private String lastName;
@NotEmpty(message = "Email為必填項目")
必須符合Email的格式,例如xxx@xxx.xxx,不然會顯示錯誤
@Email(message = "請填入正確的Email")
private String email;
public Customer(){
}
public Customer(String id, String firstName, String lastName, String email) {
this.id = id;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
在寫entity時,有些人可能注意到了,id的資料形態被設定成String而不是Long。
這是因為在MongoDB中,預設的id格式是UUID(Universally Unique Identifier),這裡提供一個產生的UUID的範例:560266b0-2bf0-4241-a578-66efb7c3ec80。
UUID的優勢是,可以大幅降低產生相同id的機率。
傳統ID (自增ID )在很多人同時使用的大型專案,容易發生碰撞(collision),因為它們是根據時間來決定給的id順序。
在資料庫中,ID是唯一的,所以不能重複。
然而,當許多人同時在新建內容時,可能在同一毫秒送出新增資料的請求。
這時就有可能給出相同的ID ,由於資料庫不允許重複的ID 存在,發生了collision,所以增加資料的請求會被拒絕,並回傳錯誤,最後導致使用者無法新增內容。
使用者見到無法新增資料,一定會按下按鈕,再傳送一次,但是這將導致系統狀態進一步的惡化,最終讓伺服器當機,全部人不能新增資料。
為了解決collision的問題,設計了UUID。
UUID是隨機產生的,不會遞增,這代表即使在同一毫秒送出請求,產生的UUID還是會有很大的不同。
UUID的概念加上隨機的成分,把collision發生的可能性降到最低,需要每秒產生10億個UUID並且持續85年,才有可能發生一次collision。
儘管UUID在避免collision的部分有強大的優勢,但是它也有缺點。
其中一個缺點是,查詢的速度比起傳統的ID慢。
由於UUID充滿隨機性,我們無法提前得知某個UUID是否存在資料庫中,因此只能一個一個確認。
而傳統的id是遞增的,有順序的,可以快速定位。
舉個例子
總結一下
透過繼承MongoRepository來使用Spring Data MongoDB,這樣可以輕鬆的對 MongoDB 進行資料庫的操作。
和之前使用Spring Data JPA時相同,因為我們換成Spring Data MongoDB,所以我們改為extends MongoRepository。
Customer是Document,String是id的資料型態。
//CustomerRepository.java
public interface CustomerRepository extends MongoRepository<Customer, String> {
}
實作建立客戶、取得全部的客戶、取得指定id的客戶、修改客戶、刪除客戶的功能。
//CustomerService.java
@Service
public class CustomerService {
private final CustomerRepository customerRepository;
public CustomerService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
//建立客戶並儲存
public void createCustomer(Customer customer) {
customerRepository.save(customer);
}
//取得全部的客戶
public List<Customer> getAllCustomers() {
return customerRepository.findAll();
}
//取得指定id的客戶
public Customer getCustomerById(String id) {
return customerRepository.findById(id).orElse(null);
}
//修改客戶內容
public void updateCustomer(Customer customer) {
Customer oldCustomer = customerRepository.findById(customer.getId()).orElse(null);
if (oldCustomer != null) {
oldCustomer.setFirstName(customer.getFirstName());
oldCustomer.setLastName(customer.getLastName());
oldCustomer.setEmail(customer.getEmail());
customerRepository.save(oldCustomer);
}
}
//刪除客戶
public void deleteCustomer(String id) {
customerRepository.deleteById(id);
}
}
之前的專案我們都是使用RestController,但是這次不一樣了,因為我們使用了thymeleaf作為前端,因此要使用Controller來呈現我們的網頁內容。
注意!是Controller,不是RestController
//CustomerController.java
@Controller
public class CustomerController {
private final CustomerService customerService;
public CustomerController(CustomerService customerService) {
this.customerService = customerService;
}
@GetMapping("/")
public String listCustomers(Model model) {
List<Customer> customers = customerService.getAllCustomers()
我們需要使用model才能把資料從後端傳給前端,"customers"是識別用的名稱
model.addAttribute("customers", customers);
Thymeleaf使用到的html檔案都需要放在src/main/resources/templates中
所以以下的程式碼,會將src/main/resources/templates/index.html的內容顯示在網頁上
return "index";
}
@GetMapping("/new")
public String newCustomer(Model model) {
創建一個空的新客戶,並傳送到前端
Customer customer = new Customer();
model.addAttribute("customer", customer);
顯示new_customer.html的內容
return "new_customer";
}
@PostMapping("/new")
public String saveCustomer(Model model,
Valid用來在前端驗證內容
ModelAttribute用來接收前端中customer的資料
@Valid @ModelAttribute("customer") Customer customer,
BindingResult bindingResult) {
如果資料不符合規定,例如留空或email格式錯誤,就會將document設定的訊息顯示在網頁上
if(bindingResult.hasErrors()) {
model.addAttribute("customer", customer);
return "new_customer";
}
符合規定就可以直接新增
customerService.createCustomer(customer);
導回http://localhost:8080/
return "redirect:/";
}
@GetMapping("/view/{id}")
public String viewCustomer(Model model, @PathVariable("id") String id) {
將資料庫的客戶內容顯示在網頁上
Customer customer = customerService.getCustomerById(id);
model.addAttribute("customer", customer);
顯示view_customer.html的內容
return "view_customer";
}
@GetMapping("/edit/{id}")
public String editCustomer(Model model, @PathVariable("id") String id) {
將資料庫原本的客戶內容顯示在網頁上
Customer customer = customerService.getCustomerById(id);
model.addAttribute("customer", customer);
顯示edit_customer.html的內容
return "edit_customer";
}
@PostMapping("/edit/{id}")
public String updateCustomer(Model model,
@PathVariable("id") String id,
Valid用來在前端驗證內容
ModelAttribute用來接收前端中customer的資料
@Valid @ModelAttribute("customer") Customer customer,
BindingResult bindingResult) {
如果資料不符合規定,例如留空或email格式錯誤,就會將document設定的訊息顯示在網頁上
if(bindingResult.hasErrors()) {
model.addAttribute("customer", customer);
return "edit_customer";
}
customerService.updateCustomer(customer);
導回http://localhost:8080/
return "redirect:/";
}
@GetMapping("/delete/{id}")
public String deleteCustomer(@PathVariable("id") String id) {
customerService.deleteCustomer(id);
//導回http://localhost:8080/
return "redirect:/";
}
}
專案後端的部分結束了,可以從程式碼的部分觀察到,使用thymeleaf除了要寫前端的程式碼,還要另外寫後端如何去和前端交流的部分。